package org.example.erpapiservicespringmongoimpl;

import org.example.erpapicommon.Util.BasePage;
import org.example.erpapicommon.Util.Pageable;
import org.example.erpapicommon.Util.Param.ProductQueryParam;
import org.example.erpapicommon.model.Product;
import org.example.erpapicommon.model.SimpleCategory;
import org.example.erpapicommon.model.TProduct;
import org.example.erpapicommon.service.ProductService;
import org.example.erpapiservicespringmongoimpl.doc.CategoryDoc;
import org.example.erpapiservicespringmongoimpl.doc.ProductDoc;
import org.example.erpapiservicespringmongoimpl.mapper.CategoryMapper;
import org.example.erpapiservicespringmongoimpl.mapper.ProductMapper;
import org.example.erpapiservicespringmongoimpl.mapper.ProductPageMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Component
public class ProductServiceMongoImpl implements ProductService {
    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    ProductMapper productMapper;
    @Autowired
    CategoryMapper categoryMapper;
    @Autowired
    ProductPageMapper productPageMapper;
    @Override
    @Cacheable(value="erp_api", key = "'product_'+#id")
    public Optional<Product> getProductById(Integer id) {
        ProductDoc productDoc = mongoTemplate.findById(id, ProductDoc.class);
        if (productDoc==null){
            return Optional.empty();
        }
        Product product = productMapper.fromDoc(productDoc);
        List<CategoryDoc> categoryList =new ArrayList<>();
        categoryList.add(productDoc.getCategory().getLevel1());
        categoryList.add(productDoc.getCategory().getLevel2());
        categoryList.add(productDoc.getCategory().getLevel3());
        product.setCategory(categoryMapper.fromCategoriesToSimpleCategories(categoryList));
        product.setCategoryId(productDoc.getCategory().getLevel3().getId());
        product.setCategoryName(productDoc.getCategory().getLevel3().getName());
        return Optional.of(product);
    }
    @Override
    @Cacheable(value = "erp_api",key = "'pageProducts_'+ #param.hashCode()", condition = "#param != null")
    public Pageable<TProduct> PageProducts(ProductQueryParam param) {
        List<Criteria> criteriaList =new ArrayList<>();
        String name = param.getName();
        if (name!=null&&!name.isBlank()){
            criteriaList.add(Criteria.where("name").regex(name));
        }
        String brand = param.getBrand();
        if (brand!=null&&!brand.isBlank()){
            criteriaList.add(Criteria.where("brand").regex(brand));
        }
        String categoryId = param.getCategoryId();
        if (categoryId!=null&&!categoryId.isBlank()){
            criteriaList.add(Criteria.where("category.level1.id").is(Integer.valueOf(categoryId)));
        }
        Integer lowPrice = param.getLowPrice();
        if (lowPrice!=null&&lowPrice>0){
            criteriaList.add(Criteria.where("unitPrice").gte(lowPrice));
        }
        Integer highPrice = param.getHighPrice();
        if (highPrice!=null&&highPrice>0){
            criteriaList.add(Criteria.where("unitPrice").lte(highPrice));
        }
        if (param.isUnitsInStock()){
            criteriaList.add(Criteria.where("unitsInStock").gt(0));
        }
        Query query = new Query();
        Criteria criteria = new Criteria();
        if (!criteriaList.isEmpty()){
            criteria.andOperator(criteriaList);
        }
        query.addCriteria(criteria);
        long totalCount = mongoTemplate.count(query, ProductDoc.class);
        int pageSize = param.getPageSize();
        int pageNumber = param.getPageNumber();
        int totalPage = PageUtil.getTotalPage((int)totalCount, pageSize);
        int offset = PageUtil.getOffset(pageNumber, pageSize);
        query.skip(offset).limit(pageSize);
        List<ProductDoc> productDocList = mongoTemplate.find(query, ProductDoc.class);
        List<TProduct> tProductList = productPageMapper.fromDocsToTProducts(productDocList);
        BasePage<TProduct> tProductPageable = new BasePage<>(tProductList, pageNumber, pageSize, totalPage, (int)totalCount);
        return tProductPageable;
    }

    @Override
    @Cacheable(value = "erp_api",key = "'pageProducts_'+ #param.hashCode()+'bySupplier_'+#supplierId", condition = "#param != null")
    public Pageable<TProduct> PageProductsbySupplierId(ProductQueryParam param, Integer supplierId) {
        List<Criteria> criteriaList =new ArrayList<>();
        criteriaList.add(Criteria.where("suppliers").elemMatch(Criteria.where("id").is(supplierId)));
        String name = param.getName();
        if (name!=null&&!name.isBlank()){
            criteriaList.add(Criteria.where("name").regex(name));
        }
        String brand = param.getBrand();
        if (brand!=null&&!brand.isBlank()){
            criteriaList.add(Criteria.where("brand").regex(brand));
        }
        String categoryId = param.getCategoryId();
        if (categoryId!=null&&!categoryId.isBlank()){
            criteriaList.add(Criteria.where("category.level1._Id").is(Integer.valueOf(categoryId)));
        }
        Integer lowPrice = param.getLowPrice();
        if (lowPrice!=null&&lowPrice>0){
            criteriaList.add(Criteria.where("unitPrice").gte(lowPrice));
        }
        Integer highPrice = param.getHighPrice();
        if (highPrice!=null&&highPrice>0){
            criteriaList.add(Criteria.where("unitPrice").lte(highPrice));
        }
        if (param.isUnitsInStock()){
            criteriaList.add(Criteria.where("unitsInStock").gt(0));
        }
        Query query = new Query();
        Criteria criteria = new Criteria();
        if (!criteriaList.isEmpty()){
            criteria.andOperator(criteriaList);
        }
        query.addCriteria(criteria);
        long totalCount = mongoTemplate.count(query, ProductDoc.class);
        int pageSize = param.getPageSize();
        int pageNumber = param.getPageNumber();
        int totalPage = PageUtil.getTotalPage((int)totalCount, pageSize);
        int offset = PageUtil.getOffset(pageNumber, pageSize);
        query.skip(offset).limit(pageSize);
        List<ProductDoc> productDocList = mongoTemplate.find(query, ProductDoc.class);
        List<TProduct> tProductList = productPageMapper.fromDocsToTProducts(productDocList);

        BasePage<TProduct> tProductPageable = new BasePage<>(tProductList, pageNumber, pageSize, totalPage, (int)totalCount);
        return tProductPageable;
    }
}
